/**
 * NEURON IIoT System for Industry 4.0
 * Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 **/

/*
 * DO NOT EDIT THIS FILE MANUALLY!
 * It was automatically generated by `json-autotype`.
 */

#include <stdlib.h>
#include <string.h>

#include <jansson.h>

#include "json/json.h"

#include "define.h"
#include "msg.h"
#include "tag.h"

#include "json/neu_json_rw.h"

int neu_json_encode_read_resp(void *json_object, void *param)
{
    int                   ret  = 0;
    neu_json_read_resp_t *resp = (neu_json_read_resp_t *) param;

    void *                    tag_array = neu_json_array();
    neu_json_read_resp_tag_t *p_tag     = resp->tags;
    for (int i = 0; i < resp->n_tag; i++) {
        neu_json_elem_t tag_elems[2 + NEU_TAG_META_SIZE] = { 0 };
        int             if_precision                     = 0;

        tag_elems[0].name      = "name";
        tag_elems[0].t         = NEU_JSON_STR;
        tag_elems[0].v.val_str = p_tag->name;

        if (p_tag->error != 0) {
            tag_elems[1].name      = "error";
            tag_elems[1].t         = NEU_JSON_INT;
            tag_elems[1].v.val_int = p_tag->error;
        } else {
            tag_elems[1].name      = "value";
            tag_elems[1].t         = p_tag->t;
            tag_elems[1].v         = p_tag->value;
            tag_elems[1].precision = p_tag->precision;
            tag_elems[1].bias      = p_tag->datatag.bias;

            if (p_tag->t == NEU_JSON_FLOAT || p_tag->t == NEU_JSON_DOUBLE) {
                if_precision      = 1;
                tag_elems[2].name = "transferPrecision";
                tag_elems[2].t    = NEU_JSON_INT;
                tag_elems[2].v.val_int =
                    p_tag->precision > 0 ? p_tag->precision : 1;
            }
        }

        for (int k = 0; k < p_tag->n_meta; k++) {
            tag_elems[if_precision + 2 + k].name = p_tag->metas[k].name;
            tag_elems[if_precision + 2 + k].t    = p_tag->metas[k].t;
            tag_elems[if_precision + 2 + k].v    = p_tag->metas[k].value;
        }

        tag_array = neu_json_encode_array(tag_array, tag_elems,
                                          2 + if_precision + p_tag->n_meta);
        p_tag++;
    }

    neu_json_elem_t resp_elems[] = { {
        .name         = "tags",
        .t            = NEU_JSON_OBJECT,
        .v.val_object = tag_array,
    } };
    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));

    return ret;
}

int neu_json_encode_read_paginate_resp(void *json_object, void *param)
{
    int                            ret = 0;
    neu_json_read_paginate_resp_t *resp =
        (neu_json_read_paginate_resp_t *) param;

    void *                             tag_array = neu_json_array();
    neu_json_read_paginate_resp_tag_t *p_tag     = resp->tags;

    for (int i = 0; i < resp->n_tag; i++) {
        neu_json_elem_t tag_elems[10 + NEU_TAG_META_SIZE] = { 0 };
        int             if_precision                      = 0;

        tag_elems[0].name      = "name";
        tag_elems[0].t         = NEU_JSON_STR;
        tag_elems[0].v.val_str = p_tag->name;

        tag_elems[1].name      = "type";
        tag_elems[1].t         = NEU_JSON_INT;
        tag_elems[1].v.val_int = p_tag->datatag.type;

        tag_elems[2].name      = "address";
        tag_elems[2].t         = NEU_JSON_STR;
        tag_elems[2].v.val_str = p_tag->datatag.address;

        tag_elems[3].name      = "attribute";
        tag_elems[3].t         = NEU_JSON_INT;
        tag_elems[3].v.val_int = p_tag->datatag.attribute;

        tag_elems[4].name      = "description";
        tag_elems[4].t         = NEU_JSON_STR;
        tag_elems[4].v.val_str = p_tag->datatag.description;

        tag_elems[5].name      = "precision";
        tag_elems[5].t         = NEU_JSON_INT;
        tag_elems[5].v.val_int = p_tag->datatag.precision;

        tag_elems[6].name         = "decimal";
        tag_elems[6].t            = NEU_JSON_DOUBLE;
        tag_elems[6].v.val_double = p_tag->datatag.decimal;

        tag_elems[7].name         = "bias";
        tag_elems[7].t            = NEU_JSON_DOUBLE;
        tag_elems[7].v.val_double = p_tag->datatag.bias;

        if (p_tag->error != 0) {
            tag_elems[8].name      = "error";
            tag_elems[8].t         = NEU_JSON_INT;
            tag_elems[8].v.val_int = p_tag->error;
        } else {
            tag_elems[8].name      = "value";
            tag_elems[8].t         = p_tag->t;
            tag_elems[8].v         = p_tag->value;
            tag_elems[8].precision = p_tag->precision;
            tag_elems[8].bias      = p_tag->datatag.bias;

            if (p_tag->t == NEU_JSON_FLOAT || p_tag->t == NEU_JSON_DOUBLE) {
                if_precision      = 1;
                tag_elems[9].name = "transferPrecision";
                tag_elems[9].t    = NEU_JSON_INT;
                tag_elems[9].v.val_int =
                    p_tag->precision > 0 ? p_tag->precision : 1;
            }
        }

        void *attributes_object = neu_json_encode_new();
        for (int k = 0; k < p_tag->n_meta; k++) {
            neu_json_elem_t meta_elem = { 0 };
            meta_elem.name            = p_tag->metas[k].name;
            meta_elem.t               = p_tag->metas[k].t;
            meta_elem.v               = p_tag->metas[k].value;
            neu_json_encode_field(attributes_object, &meta_elem, 1);
        }

        tag_elems[if_precision + 9].name         = "attributes";
        tag_elems[if_precision + 9].t            = NEU_JSON_OBJECT;
        tag_elems[if_precision + 9].v.val_object = attributes_object;

        tag_array =
            neu_json_encode_array(tag_array, tag_elems, 10 + if_precision);

        free(p_tag->datatag.name);
        free(p_tag->datatag.address);
        free(p_tag->datatag.description);

        p_tag++;
    }

    void *          meta_object  = neu_json_encode_new();
    neu_json_elem_t meta_elems[] = {
        { .name      = "currentPage",
          .t         = NEU_JSON_INT,
          .v.val_int = resp->meta.current_page },
        { .name      = "pageSize",
          .t         = NEU_JSON_INT,
          .v.val_int = resp->meta.page_size },
        { .name = "total", .t = NEU_JSON_INT, .v.val_int = resp->meta.total }
    };
    neu_json_encode_field(meta_object, meta_elems, 3);

    neu_json_elem_t resp_elems[] = {
        { .name = "meta", .t = NEU_JSON_OBJECT, .v.val_object = meta_object },
        { .name = "items", .t = NEU_JSON_OBJECT, .v.val_object = tag_array },
    };
    ret = neu_json_encode_field(json_object, resp_elems, 2);

    return ret;
}

int neu_json_encode_test_read_tag_resp(void *json_object, void *param)
{
    int                       ret       = 0;
    neu_resp_test_read_tag_t *resp      = (neu_resp_test_read_tag_t *) param;
    neu_json_elem_t           resp_elem = { 0 };

    if (resp->type == NEU_TYPE_ERROR) {
        resp_elem.name      = "error";
        resp_elem.t         = NEU_JSON_INT;
        resp_elem.v.val_int = resp->error;
    } else {
        resp_elem.name = "value";
        resp_elem.t    = resp->t;
        resp_elem.v    = resp->value;
    }

    ret = neu_json_encode_field(json_object, &resp_elem, 1);

    if (resp->t == NEU_JSON_STR) {
        free(resp->value.val_str);
    }

    return ret;
}

int neu_json_encode_read_resp1(void *json_object, void *param)
{
    int                   ret  = 0;
    neu_json_read_resp_t *resp = (neu_json_read_resp_t *) param;

    void *values = neu_json_encode_new();
    void *errors = neu_json_encode_new();
    void *metas  = neu_json_encode_new();

    neu_json_read_resp_tag_t *p_tag = resp->tags;
    for (int i = 0; i < resp->n_tag; i++) {
        neu_json_elem_t tag_elem = { 0 };

        if (p_tag->error == 0) {
            tag_elem.name      = p_tag->name;
            tag_elem.t         = p_tag->t;
            tag_elem.v         = p_tag->value;
            tag_elem.precision = p_tag->precision;
            tag_elem.bias      = p_tag->datatag.bias;
            neu_json_encode_field(values, &tag_elem, 1);
        } else {
            tag_elem.name      = p_tag->name;
            tag_elem.t         = NEU_JSON_INT;
            tag_elem.v.val_int = p_tag->error;
            neu_json_encode_field(errors, &tag_elem, 1);
        }

        if (p_tag->n_meta > 0) {
            void *meta = neu_json_encode_new();
            for (int k = 0; k < p_tag->n_meta; k++) {
                neu_json_elem_t meta_elem = { 0 };
                meta_elem.name            = p_tag->metas[k].name;
                meta_elem.t               = p_tag->metas[k].t;
                meta_elem.v               = p_tag->metas[k].value;
                neu_json_encode_field(meta, &meta_elem, 1);
            }

            neu_json_elem_t meta_elem = { 0 };
            meta_elem.name            = p_tag->name;
            meta_elem.t               = NEU_JSON_OBJECT;
            meta_elem.v.val_object    = meta;
            neu_json_encode_field(metas, &meta_elem, 1);
        }

        p_tag++;
    }

    neu_json_elem_t resp_elems[] = {
        {
            .name         = "values",
            .t            = NEU_JSON_OBJECT,
            .v.val_object = values,
        },
        {
            .name         = "errors",
            .t            = NEU_JSON_OBJECT,
            .v.val_object = errors,

        },
        {
            .name         = "metas",
            .t            = NEU_JSON_OBJECT,
            .v.val_object = metas,

        },
    };

    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));
    return ret;
}

int neu_json_encode_read_resp2(void *json_object, void *param)
{
    int                   ret  = 0;
    neu_json_read_resp_t *resp = (neu_json_read_resp_t *) param;

    void *                    tag_array = neu_json_array();
    neu_json_read_resp_tag_t *p_tag     = resp->tags;
    for (int i = 0; i < resp->n_tag; i++) {
        neu_json_elem_t tag_elems[2 + NEU_TAG_META_SIZE] = { 0 };

        tag_elems[0].name      = "name";
        tag_elems[0].t         = NEU_JSON_STR;
        tag_elems[0].v.val_str = p_tag->name;

        if (p_tag->error != 0) {
            tag_elems[1].name      = "error";
            tag_elems[1].t         = NEU_JSON_INT;
            tag_elems[1].v.val_int = p_tag->error;
        } else {
            tag_elems[1].name      = "value";
            tag_elems[1].t         = p_tag->t;
            tag_elems[1].v         = p_tag->value;
            tag_elems[1].precision = p_tag->precision;
        }

        for (int k = 0; k < p_tag->n_meta; k++) {
            tag_elems[2 + k].name = p_tag->metas[k].name;
            tag_elems[2 + k].t    = p_tag->metas[k].t;
            tag_elems[2 + k].v    = p_tag->metas[k].value;
        }

        tag_array =
            neu_json_encode_array(tag_array, tag_elems, 2 + p_tag->n_meta);
        p_tag++;
    }

    neu_json_elem_t resp_elems[] = { {
        .name         = "tags",
        .t            = NEU_JSON_OBJECT,
        .v.val_object = tag_array,
    } };
    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));

    return ret;
}

int neu_json_encode_read_resp_ecp(void *json_object, void *param)
{
    int                   ret      = 0;
    int                   has_data = 0;
    neu_json_read_resp_t *resp     = (neu_json_read_resp_t *) param;

    void *                    tag_array = neu_json_array();
    neu_json_read_resp_tag_t *p_tag     = resp->tags;
    for (int i = 0; i < resp->n_tag; i++) {
        neu_json_elem_t tag_elems[2 + NEU_TAG_META_SIZE] = { 0 };

        tag_elems[0].name      = "name";
        tag_elems[0].t         = NEU_JSON_STR;
        tag_elems[0].v.val_str = p_tag[i].name;

        if (p_tag[i].error != 0) {
            continue;
        } else {
            tag_elems[1].name      = "value";
            tag_elems[1].t         = p_tag[i].t;
            tag_elems[1].v         = p_tag[i].value;
            tag_elems[1].precision = p_tag[i].precision;

            tag_elems[2].name      = "type";
            tag_elems[2].t         = NEU_JSON_INT;
            tag_elems[2].v.val_int = neu_json_type_transfer(p_tag[i].t);
        }

        for (int k = 0; k < p_tag[i].n_meta; k++) {
            tag_elems[3 + k].name = p_tag[i].metas[k].name;
            tag_elems[3 + k].t    = p_tag[i].metas[k].t;
            tag_elems[3 + k].v    = p_tag[i].metas[k].value;
        }

        tag_array = neu_json_encode_array_ecp(tag_array, tag_elems,
                                              3 + p_tag[i].n_meta);
        has_data  = 1;
    }

    neu_json_elem_t resp_elems[] = { {
        .name         = "tags",
        .t            = NEU_JSON_OBJECT,
        .v.val_object = tag_array,
    } };
    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));

    if (!has_data) {
        return -2;
    }

    return ret;
}

int neu_json_decode_write_req(char *buf, neu_json_write_req_t **result)
{
    void *json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        return -1;
    }

    int ret = neu_json_decode_write_req_json(json_obj, result);
    neu_json_decode_free(json_obj);
    return ret;
}

int decode_write_req_json(void *json_obj, neu_json_write_req_t *req)
{
    int ret = 0;

    neu_json_elem_t req_elems[] = {
        {
            .name = "node",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "group",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "tag",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "value",
            .t    = NEU_JSON_VALUE,
        },
        {
            .name      = "precision",
            .t         = NEU_JSON_INT,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
    };

    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);
    if (ret != 0) {
        goto error;
    }

    if (req_elems[4].v.val_int > 0) {
        req_elems[3].t            = NEU_JSON_DOUBLE;
        req_elems[3].v.val_double = (double_t) req_elems[3].v.val_int;
    }

    req->node  = req_elems[0].v.val_str;
    req->group = req_elems[1].v.val_str;
    req->tag   = req_elems[2].v.val_str;
    req->t     = req_elems[3].t;
    req->value = req_elems[3].v;

    if (req->t == NEU_JSON_OBJECT) {
        json_incref(req->value.val_object);
    }

    return ret;

error:
    free(req_elems[0].v.val_str);
    free(req_elems[1].v.val_str);
    free(req_elems[2].v.val_str);
    return ret;
}

int neu_json_decode_write_req_json(void *                 json_obj,
                                   neu_json_write_req_t **result)
{
    neu_json_write_req_t *req = calloc(1, sizeof(neu_json_write_req_t));
    if (req == NULL) {
        return -1;
    }

    int ret = decode_write_req_json(json_obj, req);
    if (0 == ret) {
        *result = req;
    } else {
        free(req);
    }

    return ret;
}

void neu_json_decode_write_req_free(neu_json_write_req_t *req)
{
    free(req->group);
    free(req->node);
    free(req->tag);
    if (req->t == NEU_JSON_STR) {
        free(req->value.val_str);
    }
    if (req->t == NEU_JSON_ARRAY_STR && req->value.val_array_str.length > 0) {
        free(req->value.val_array_str.p_strs);
    }
    if (req->t == NEU_JSON_ARRAY_INT8 && req->value.val_array_int8.length > 0) {
        free(req->value.val_array_int8.i8s);
    }
    if (req->t == NEU_JSON_ARRAY_UINT8 &&
        req->value.val_array_uint8.length > 0) {
        free(req->value.val_array_uint8.u8s);
    }
    if (req->t == NEU_JSON_ARRAY_INT16 &&
        req->value.val_array_int16.length > 0) {
        free(req->value.val_array_int16.i16s);
    }
    if (req->t == NEU_JSON_ARRAY_UINT16 &&
        req->value.val_array_uint16.length > 0) {
        free(req->value.val_array_uint16.u16s);
    }
    if (req->t == NEU_JSON_ARRAY_INT32 &&
        req->value.val_array_int32.length > 0) {
        free(req->value.val_array_int32.i32s);
    }
    if (req->t == NEU_JSON_ARRAY_UINT32 &&
        req->value.val_array_uint32.length > 0) {
        free(req->value.val_array_uint32.u32s);
    }
    if (req->t == NEU_JSON_ARRAY_INT64 &&
        req->value.val_array_int64.length > 0) {
        free(req->value.val_array_int64.i64s);
    }
    if (req->t == NEU_JSON_ARRAY_UINT64 &&
        req->value.val_array_uint64.length > 0) {
        free(req->value.val_array_uint64.u64s);
    }
    if (req->t == NEU_JSON_ARRAY_FLOAT &&
        req->value.val_array_float.length > 0) {
        free(req->value.val_array_float.f32s);
    }
    if (req->t == NEU_JSON_ARRAY_DOUBLE &&
        req->value.val_array_double.length > 0) {
        free(req->value.val_array_double.f64s);
    }
    if (req->t == NEU_JSON_ARRAY_BOOL && req->value.val_array_bool.length > 0) {
        free(req->value.val_array_bool.bools);
    }

    free(req);
}

int neu_json_decode_write_tags_req(char *                      buf,
                                   neu_json_write_tags_req_t **result)
{
    void *json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        return -1;
    }

    int ret = neu_json_decode_write_tags_req_json(json_obj, result);
    neu_json_decode_free(json_obj);
    return ret;
}

static int decode_write_tags_req_json(void *                     json_obj,
                                      neu_json_write_tags_req_t *req)
{
    int ret = 0;

    neu_json_elem_t req_elems[] = {
        {
            .name = "node",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "group",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "tags",
            .t    = NEU_JSON_OBJECT,
        },
    };
    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);
    if (ret != 0) {
        return -1;
    }

    req->node  = req_elems[0].v.val_str;
    req->group = req_elems[1].v.val_str;

    req->n_tag = neu_json_decode_array_size_by_json(json_obj, "tags");
    if (req->n_tag <= 0) {
        return -1;
    }

    req->tags = calloc(req->n_tag, sizeof(neu_json_write_tags_elem_t));
    for (int i = 0; i < req->n_tag; i++) {
        neu_json_elem_t v_elems[] = {
            {
                .name    = "tag",
                .op_name = "name",
                .t       = NEU_JSON_STR,
            },
            {
                .name = "value",
                .t    = NEU_JSON_VALUE,
            },
        };

        ret = neu_json_decode_array_by_json(
            json_obj, "tags", i, NEU_JSON_ELEM_SIZE(v_elems), v_elems);
        req->tags[i].tag   = v_elems[0].v.val_str;
        req->tags[i].t     = v_elems[1].t;
        req->tags[i].value = v_elems[1].v;

        if (req->tags[i].t == NEU_JSON_OBJECT) {
            json_incref(req->tags[i].value.val_object);
        }

        if (ret != 0) {
            for (; i >= 0; --i) {
                free(req->tags[i].tag);
                if (NEU_JSON_STR == req->tags[i].t) {
                    free(req->tags[i].value.val_str);
                }
            }
            free(req->node);
            free(req->group);
            free(req->tags);
            req->tags = NULL;
            return ret;
        }
    }

    return 0;
}

int neu_json_decode_write_tags_req_json(void *                      json_obj,
                                        neu_json_write_tags_req_t **result)
{
    neu_json_write_tags_req_t *req =
        calloc(1, sizeof(neu_json_write_tags_req_t));
    if (req == NULL) {
        return -1;
    }

    int ret = decode_write_tags_req_json(json_obj, req);
    if (0 == ret) {
        *result = req;
    } else {
        free(req);
    }

    return ret;
}

void neu_json_decode_write_tags_req_free(neu_json_write_tags_req_t *req)
{
    free(req->group);
    free(req->node);

    for (int i = 0; i < req->n_tag; i++) {
        free(req->tags[i].tag);
        if (req->tags[i].t == NEU_JSON_STR) {
            free(req->tags[i].value.val_str);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_INT8 &&
            req->tags[i].value.val_array_int8.length > 0) {
            free(req->tags[i].value.val_array_int8.i8s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_UINT8 &&
            req->tags[i].value.val_array_uint8.length > 0) {
            free(req->tags[i].value.val_array_uint8.u8s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_INT16 &&
            req->tags[i].value.val_array_int16.length > 0) {
            free(req->tags[i].value.val_array_int16.i16s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_UINT16 &&
            req->tags[i].value.val_array_uint16.length > 0) {
            free(req->tags[i].value.val_array_uint16.u16s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_INT32 &&
            req->tags[i].value.val_array_int32.length > 0) {
            free(req->tags[i].value.val_array_int32.i32s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_UINT32 &&
            req->tags[i].value.val_array_uint32.length > 0) {
            free(req->tags[i].value.val_array_uint32.u32s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_INT64 &&
            req->tags[i].value.val_array_int64.length > 0) {
            free(req->tags[i].value.val_array_int64.i64s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_UINT64 &&
            req->tags[i].value.val_array_uint64.length > 0) {
            free(req->tags[i].value.val_array_uint64.u64s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_FLOAT &&
            req->tags[i].value.val_array_float.length > 0) {
            free(req->tags[i].value.val_array_float.f32s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_DOUBLE &&
            req->tags[i].value.val_array_double.length > 0) {
            free(req->tags[i].value.val_array_double.f64s);
        }
        if (req->tags[i].t == NEU_JSON_ARRAY_BOOL &&
            req->tags[i].value.val_array_bool.length > 0) {
            free(req->tags[i].value.val_array_bool.bools);
        }
    }
    free(req->tags);
    free(req);
}

int neu_json_decode_write(char *buf, neu_json_write_t **result)
{
    neu_json_write_t *req = calloc(1, sizeof(*req));
    if (NULL == req) {
        return -1;
    }

    void *json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        free(req);
        return -1;
    }

    int ret = 0;
    if (NULL == json_object_get(json_obj, "tags")) {
        req->singular = true;
        ret           = decode_write_req_json(json_obj, &req->single);
    } else {
        req->singular = false;
        ret           = decode_write_tags_req_json(json_obj, &req->plural);
    }

    if (0 == ret) {
        *result = req;
    } else {
        free(req);
    }

    neu_json_decode_free(json_obj);
    return ret;
}

void neu_json_decode_write_free(neu_json_write_t *req)
{
    if (req) {
        if (req->singular) {
            neu_json_decode_write_req_free((neu_json_write_req_t *) req);
        } else {
            neu_json_decode_write_tags_req_free(
                (neu_json_write_tags_req_t *) req);
        }
    }
}

int neu_json_decode_read_req(char *buf, neu_json_read_req_t **result)
{
    int   ret      = 0;
    void *json_obj = NULL;

    json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        return -1;
    }

    neu_json_read_req_t *req = calloc(1, sizeof(neu_json_read_req_t));
    if (req == NULL) {
        neu_json_decode_free(json_obj);
        return -1;
    }

    neu_json_elem_t req_elems[] = {
        {
            .name = "node",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "group",
            .t    = NEU_JSON_STR,
        },
        {
            .name      = "sync",
            .t         = NEU_JSON_BOOL,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "query",
            .t         = NEU_JSON_OBJECT,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name                   = "tags",
            .t                      = NEU_JSON_ARRAY_STR,
            .attribute              = NEU_JSON_ATTRIBUTE_OPTIONAL,
            .v.val_array_str.length = 0,
            .v.val_array_str.p_strs = NULL,
        },
    };

    neu_json_elem_t query_elems[] = {
        {
            .name      = "name",
            .t         = NEU_JSON_STR,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "description",
            .t         = NEU_JSON_STR,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
    };

    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);
    if (ret != 0) {
        goto error;
    }

    req->node   = req_elems[0].v.val_str;
    req->group  = req_elems[1].v.val_str;
    req->sync   = req_elems[2].v.val_bool;
    req->n_tags = req_elems[4].v.val_array_str.length;
    req->tags   = req_elems[4].v.val_array_str.p_strs;

    if (req_elems[3].v.val_object) {
        ret = neu_json_decode_by_json(req_elems[3].v.val_object,
                                      NEU_JSON_ELEM_SIZE(query_elems),
                                      query_elems);
        if (ret != 0) {
            goto error;
        }
        req->name = query_elems[0].v.val_str;
        req->desc = query_elems[1].v.val_str;
    }

    *result = req;
    neu_json_decode_free(json_obj);
    return ret;

error:
    free(query_elems[0].v.val_str);
    free(query_elems[1].v.val_str);
    free(req_elems[0].v.val_str);
    free(req_elems[1].v.val_str);
    free(req);
    if (json_obj != NULL) {
        neu_json_decode_free(json_obj);
    }
    return ret;
}

void neu_json_decode_read_req_free(neu_json_read_req_t *req)
{
    free(req->group);
    free(req->node);
    if (req->n_tags > 0) {
        for (int i = 0; i < req->n_tags; i++) {
            if (req->tags[i] != NULL) {
                free(req->tags[i]);
            }
        }
        free(req->tags);
    }

    free(req);
}

int neu_json_decode_read_paginate_req(char *                         buf,
                                      neu_json_read_paginate_req_t **result)
{
    int   ret      = 0;
    void *json_obj = NULL;

    json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        return -1;
    }

    neu_json_read_paginate_req_t *req =
        calloc(1, sizeof(neu_json_read_paginate_req_t));
    if (req == NULL) {
        neu_json_decode_free(json_obj);
        return -1;
    }

    neu_json_elem_t req_elems[] = {
        {
            .name = "node",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "group",
            .t    = NEU_JSON_STR,
        },
        {
            .name      = "sync",
            .t         = NEU_JSON_BOOL,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "query",
            .t         = NEU_JSON_OBJECT,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
    };

    neu_json_elem_t query_elems[] = {
        {
            .name      = "name",
            .t         = NEU_JSON_STR,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "description",
            .t         = NEU_JSON_STR,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "currentPage",
            .t         = NEU_JSON_INT,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "pageSize",
            .t         = NEU_JSON_INT,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
        {
            .name      = "isError",
            .t         = NEU_JSON_BOOL,
            .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
        },
    };

    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);

    if (ret != 0) {
        goto error;
    }

    req->node  = req_elems[0].v.val_str;
    req->group = req_elems[1].v.val_str;
    req->sync  = req_elems[2].v.val_bool;

    if (req_elems[3].v.val_object) {
        ret = neu_json_decode_by_json(req_elems[3].v.val_object,
                                      NEU_JSON_ELEM_SIZE(query_elems),
                                      query_elems);

        if (ret != 0) {
            goto error;
        }
        req->name         = query_elems[0].v.val_str;
        req->desc         = query_elems[1].v.val_str;
        req->current_page = query_elems[2].v.val_int;
        req->page_size    = query_elems[3].v.val_int;
        req->is_error     = query_elems[4].v.val_bool;
    }

    *result = req;
    neu_json_decode_free(json_obj);
    return ret;

error:
    free(query_elems[0].v.val_str);
    free(query_elems[1].v.val_str);
    free(req_elems[0].v.val_str);
    free(req_elems[1].v.val_str);
    free(req);
    if (json_obj != NULL) {
        neu_json_decode_free(json_obj);
    }
    return ret;
}

void neu_json_decode_read_paginate_req_free(neu_json_read_paginate_req_t *req)
{
    free(req->group);
    free(req->node);

    free(req);
}

int neu_json_decode_test_read_tag_req(char *                         buf,
                                      neu_json_test_read_tag_req_t **result)
{
    int                           ret      = 0;
    void *                        json_obj = NULL;
    neu_json_test_read_tag_req_t *req =
        calloc(1, sizeof(neu_json_test_read_tag_req_t));
    if (req == NULL) {
        return -1;
    }

    json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        free(req);
        return -1;
    }

    neu_json_elem_t req_elems[] = {
        {
            .name = "driver",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "group",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "tag",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "address",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "attribute",
            .t    = NEU_JSON_INT,
        },
        {
            .name = "type",
            .t    = NEU_JSON_INT,
        },
        {
            .name = "precision",
            .t    = NEU_JSON_INT,
        },
        {
            .name = "decimal",
            .t    = NEU_JSON_DOUBLE,
        },
        {
            .name = "bias",
            .t    = NEU_JSON_DOUBLE,
        },
    };
    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);
    if (ret != 0) {
        goto decode_fail;
    }

    req->driver    = req_elems[0].v.val_str;
    req->group     = req_elems[1].v.val_str;
    req->tag       = req_elems[2].v.val_str;
    req->address   = req_elems[3].v.val_str;
    req->attribute = req_elems[4].v.val_int;
    req->type      = req_elems[5].v.val_int;
    req->precision = req_elems[6].v.val_int;
    req->decimal   = req_elems[7].v.val_double;
    req->bias      = req_elems[8].v.val_double;
    *result        = req;
    goto decode_exit;

decode_fail:
    free(req);
    free(req_elems[0].v.val_str);
    free(req_elems[1].v.val_str);
    free(req_elems[2].v.val_str);
    free(req_elems[3].v.val_str);
    ret = -1;

decode_exit:
    if (json_obj != NULL) {
        neu_json_decode_free(json_obj);
    }
    return ret;
}

void neu_json_decode_test_read_tag_req_free(neu_json_test_read_tag_req_t *req)
{
    free(req->driver);
    free(req->group);
    free(req->tag);
    free(req->address);
    free(req);
}

int neu_json_encode_read_periodic_resp(void *json_object, void *param)
{
    int                       ret  = 0;
    neu_json_read_periodic_t *resp = (neu_json_read_periodic_t *) param;

    neu_json_elem_t resp_elems[] = { {
                                         .name      = "node",
                                         .t         = NEU_JSON_STR,
                                         .v.val_str = resp->node,
                                     },
                                     {
                                         .name      = "group",
                                         .t         = NEU_JSON_STR,
                                         .v.val_str = resp->group,
                                     },
                                     {
                                         .name      = "timestamp",
                                         .t         = NEU_JSON_INT,
                                         .v.val_int = resp->timestamp,
                                     } };
    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));

    return ret;
}

void neu_json_metas_to_json(neu_tag_meta_t *metas, int n_meta,
                            neu_json_read_resp_tag_t *json_tag)
{
    for (int k = 0; k < n_meta; k++) {
        if (strlen(metas[k].name) > 0) {
            json_tag->metas[k].name = metas[k].name;
            switch (metas[k].value.type) {
            case NEU_TYPE_UINT8:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u8;
                break;
            case NEU_TYPE_INT8:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i8;
                break;
            case NEU_TYPE_INT16:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i16;
                break;
            case NEU_TYPE_INT32:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i32;
                break;
            case NEU_TYPE_INT64:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i64;
                break;
            case NEU_TYPE_WORD:
            case NEU_TYPE_UINT16:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u16;
                break;
            case NEU_TYPE_DWORD:
            case NEU_TYPE_UINT32:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u32;
                break;
            case NEU_TYPE_LWORD:
            case NEU_TYPE_UINT64:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u64;
                break;
            case NEU_TYPE_FLOAT:
                json_tag->metas[k].t               = NEU_JSON_FLOAT;
                json_tag->metas[k].value.val_float = metas[k].value.value.f32;
                break;
            case NEU_TYPE_DOUBLE:
                json_tag->metas[k].t                = NEU_JSON_DOUBLE;
                json_tag->metas[k].value.val_double = metas[k].value.value.d64;
                break;
            case NEU_TYPE_BOOL:
                json_tag->metas[k].t = NEU_JSON_BOOL;
                json_tag->metas[k].value.val_bool =
                    metas[k].value.value.boolean;
                break;
            case NEU_TYPE_BIT:
                json_tag->metas[k].t             = NEU_JSON_BIT;
                json_tag->metas[k].value.val_bit = metas[k].value.value.u8;
                break;
            case NEU_TYPE_STRING:
            case NEU_TYPE_TIME:
            case NEU_TYPE_DATA_AND_TIME:
                json_tag->metas[k].t             = NEU_JSON_STR;
                json_tag->metas[k].value.val_str = metas[k].value.value.str;
                break;
            default:
                break;
            }
        } else {
            break;
        }
    }
}

void neu_json_metas_to_json_paginate(
    neu_tag_meta_t *metas, int n_meta,
    neu_json_read_paginate_resp_tag_t *json_tag)
{
    for (int k = 0; k < n_meta; k++) {
        if (strlen(metas[k].name) > 0) {
            json_tag->metas[k].name = metas[k].name;
            switch (metas[k].value.type) {
            case NEU_TYPE_UINT8:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u8;
                break;
            case NEU_TYPE_INT8:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i8;
                break;
            case NEU_TYPE_INT16:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i16;
                break;
            case NEU_TYPE_INT32:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i32;
                break;
            case NEU_TYPE_INT64:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.i64;
                break;
            case NEU_TYPE_WORD:
            case NEU_TYPE_UINT16:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u16;
                break;
            case NEU_TYPE_DWORD:
            case NEU_TYPE_UINT32:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u32;
                break;
            case NEU_TYPE_LWORD:
            case NEU_TYPE_UINT64:
                json_tag->metas[k].t             = NEU_JSON_INT;
                json_tag->metas[k].value.val_int = metas[k].value.value.u64;
                break;
            case NEU_TYPE_FLOAT:
                json_tag->metas[k].t               = NEU_JSON_FLOAT;
                json_tag->metas[k].value.val_float = metas[k].value.value.f32;
                break;
            case NEU_TYPE_DOUBLE:
                json_tag->metas[k].t                = NEU_JSON_DOUBLE;
                json_tag->metas[k].value.val_double = metas[k].value.value.d64;
                break;
            case NEU_TYPE_BOOL:
                json_tag->metas[k].t = NEU_JSON_BOOL;
                json_tag->metas[k].value.val_bool =
                    metas[k].value.value.boolean;
                break;
            case NEU_TYPE_BIT:
                json_tag->metas[k].t             = NEU_JSON_BIT;
                json_tag->metas[k].value.val_bit = metas[k].value.value.u8;
                break;
            case NEU_TYPE_STRING:
            case NEU_TYPE_TIME:
            case NEU_TYPE_DATA_AND_TIME:
                json_tag->metas[k].t             = NEU_JSON_STR;
                json_tag->metas[k].value.val_str = metas[k].value.value.str;
                break;
            default:
                break;
            }
        } else {
            break;
        }
    }
}

int neu_json_decode_write_gtags_req(char *                       buf,
                                    neu_json_write_gtags_req_t **result)
{
    void *json_obj = neu_json_decode_new(buf);
    if (NULL == json_obj) {
        return -1;
    }

    int ret = neu_json_decode_write_gtags_req_json(json_obj, result);
    neu_json_decode_free(json_obj);
    return ret;
}

#include "utils/log.h"

static int decode_write_gtags_req_json(void *                      json_obj,
                                       neu_json_write_gtags_req_t *req)
{
    int ret = 0;

    neu_json_elem_t req_elems[] = {
        {
            .name = "node",
            .t    = NEU_JSON_STR,
        },
        {
            .name = "groups",
            .t    = NEU_JSON_OBJECT,
        },
    };
    ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
                                  req_elems);
    if (ret != 0) {
        return -1;
    }

    req->node = req_elems[0].v.val_str;

    req->n_group = neu_json_decode_array_size_by_json(json_obj, "groups");
    if (req->n_group <= 0) {
        return -1;
    }

    req->groups = calloc(req->n_group, sizeof(neu_json_write_gtags_elem_t));
    for (int i = 0; i < req->n_group; i++) {
        neu_json_elem_t g_elems[] = {
            {
                .name = "group",
                .t    = NEU_JSON_STR,
            },
            {
                .name = "tags",
                .t    = NEU_JSON_OBJECT,
            },
        };

        ret = neu_json_decode_array_by_json(
            json_obj, "groups", i, NEU_JSON_ELEM_SIZE(g_elems), g_elems);

        req->groups[i].group = g_elems[0].v.val_str;
        req->groups[i].n_tag = json_array_size(g_elems[1].v.val_object);
        if (req->groups[i].n_tag <= 0) {
            continue;
        }

        req->groups[i].tags =
            calloc(req->groups[i].n_tag, sizeof(neu_json_write_tags_elem_t));

        for (int k = 0; k < req->groups[i].n_tag; k++) {
            neu_json_elem_t v_elems[] = {
                {
                    .name = "tag",
                    .t    = NEU_JSON_STR,
                },
                {
                    .name = "value",
                    .t    = NEU_JSON_VALUE,
                },
            };

            ret = neu_json_decode_array_elem(g_elems[1].v.val_object, k,
                                             NEU_JSON_ELEM_SIZE(v_elems),
                                             v_elems);
            if (ret != 0) {
                for (int x = i; x >= 0; x--) {
                    free(req->groups[x].tags);
                }
                free(req->groups);
                return -1;
            }
            req->groups[i].tags[k].tag   = v_elems[0].v.val_str;
            req->groups[i].tags[k].t     = v_elems[1].t;
            req->groups[i].tags[k].value = v_elems[1].v;
        }
    }

    return 0;
}

int neu_json_decode_write_gtags_req_json(void *                       json_obj,
                                         neu_json_write_gtags_req_t **result)
{
    neu_json_write_gtags_req_t *req =
        calloc(1, sizeof(neu_json_write_gtags_req_t));
    if (req == NULL) {
        return -1;
    }

    int ret = decode_write_gtags_req_json(json_obj, req);
    if (0 == ret) {
        *result = req;
    } else {
        free(req);
    }

    return ret;
}

void neu_json_decode_write_gtags_req_free(neu_json_write_gtags_req_t *req)
{
    free(req->node);

    for (int i = 0; i < req->n_group; i++) {
        free(req->groups[i].group);

        for (int k = 0; k < req->groups[i].n_tag; k++) {
            free(req->groups[i].tags[k].tag);

            if (req->groups[i].tags[k].t == NEU_JSON_STR) {
                free(req->groups[i].tags[k].value.val_str);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_INT8 &&
                req->groups[i].tags[k].value.val_array_int8.length > 0) {
                free(req->groups[i].tags[k].value.val_array_int8.i8s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_UINT8 &&
                req->groups[i].tags[k].value.val_array_uint8.length > 0) {
                free(req->groups[i].tags[k].value.val_array_uint8.u8s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_INT16 &&
                req->groups[i].tags[k].value.val_array_int16.length > 0) {
                free(req->groups[i].tags[k].value.val_array_int16.i16s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_UINT16 &&
                req->groups[i].tags[k].value.val_array_uint16.length > 0) {
                free(req->groups[i].tags[k].value.val_array_uint16.u16s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_INT32 &&
                req->groups[i].tags[k].value.val_array_int32.length > 0) {
                free(req->groups[i].tags[k].value.val_array_int32.i32s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_UINT32 &&
                req->groups[i].tags[k].value.val_array_uint32.length > 0) {
                free(req->groups[i].tags[k].value.val_array_uint32.u32s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_INT64 &&
                req->groups[i].tags[k].value.val_array_int64.length > 0) {
                free(req->groups[i].tags[k].value.val_array_int64.i64s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_UINT64 &&
                req->groups[i].tags[k].value.val_array_uint64.length > 0) {
                free(req->groups[i].tags[k].value.val_array_uint64.u64s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_FLOAT &&
                req->groups[i].tags[k].value.val_array_float.length > 0) {
                free(req->groups[i].tags[k].value.val_array_float.f32s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_DOUBLE &&
                req->groups[i].tags[k].value.val_array_double.length > 0) {
                free(req->groups[i].tags[k].value.val_array_double.f64s);
            }
            if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_BOOL &&
                req->groups[i].tags[k].value.val_array_bool.length > 0) {
                free(req->groups[i].tags[k].value.val_array_bool.bools);
            }
        }
        if (req->groups[i].n_tag > 0) {
            free(req->groups[i].tags);
        }
    }

    free(req->groups);
    free(req);
}

int neu_json_encode_write_tags_resp(void *json_object, void *param)
{
    int                         ret   = 0;
    neu_json_write_tags_resp_t *resp  = (neu_json_write_tags_resp_t *) param;
    void *                      array = json_array();

    utarray_foreach(resp->tags, neu_resp_write_tags_ele_t *, ele)
    {
        neu_json_elem_t node_elems[] = {
            {
                .name      = "group",
                .t         = NEU_JSON_STR,
                .v.val_str = ele->group,
            },
            {
                .name      = "name",
                .t         = NEU_JSON_STR,
                .v.val_str = ele->tag,
            },
            {
                .name      = "error",
                .t         = NEU_JSON_INT,
                .v.val_int = ele->error,
            },
        };
        array = neu_json_encode_array(array, node_elems,
                                      NEU_JSON_ELEM_SIZE(node_elems));
    }

    neu_json_elem_t resp_elems[] = { {
        .name         = "tags",
        .t            = NEU_JSON_OBJECT,
        .v.val_object = array,
    } };
    ret = neu_json_encode_field(json_object, resp_elems,
                                NEU_JSON_ELEM_SIZE(resp_elems));

    return ret;
}